Skip to content

fix bugs#207

Merged
ding113 merged 20 commits intomainfrom
dev
Nov 26, 2025
Merged

fix bugs#207
ding113 merged 20 commits intomainfrom
dev

Conversation

@ding113
Copy link
Owner

@ding113 ding113 commented Nov 26, 2025

Summary

This PR fixes multiple bugs and consolidates database migrations into a single idempotent migration file.

Problem

Additionally, the migration files (0020-0025) had conflicts causing upgrade issues for users on different versions.

Solution

  • Consolidated migrations 0020-0025 into a single idempotent migration (0020_glossy_grandmaster.sql)
  • The new migration safely handles all upgrade paths:
    • Fresh installations
    • Upgrades from version 0019
    • Upgrades from conflicting migration versions
  • Uses IF NOT EXISTS and exception handling for idempotent operations
  • Cleans up conflicting migration records automatically

Changes

  • Removed: 7 conflicting migration files (0020-0024 variants)
  • Added: Single consolidated migration 0020_glossy_grandmaster.sql that:
    • Creates daily_reset_mode enum type (idempotent)
    • Updates provider timeout defaults to 0 (no limit)
    • Adds daily cost limit fields to keys and providers tables
    • Adds MCP passthrough fields to providers table
    • Adds billing model source to system_settings
    • Adds extended user limits (5h, weekly, monthly, concurrent sessions)
  • Updated: Migration snapshot to reflect consolidated schema state

Testing

  • Tested fresh database installation
  • Tested upgrade from version 0019
  • Tested upgrade from conflicting migration versions
  • Verified provider statistics accuracy
  • Verified group settings persistence
  • Verified date filtering for usage records

Related Issues

Closes #204
Closes #201
Closes #198

ding113 and others added 19 commits November 26, 2025 01:18
问题:Date 对象在 Server Action 传输时被序列化为 UTC,
后端使用服务器时区解析导致时间偏移,当天凌晨记录查不到。

修复:改用字符串直接传递本地时间,避免 Date 序列化的时区问题。

closes #198

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
用户输入分组名称后直接点击保存按钮,输入值不会被添加到标签数组中。
现在添加 onBlur 处理器,在输入框失焦时自动提交待处理的输入值。

Fixes #201

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- 新增系统设置选项,允许配置模型重定向时的计费模型来源
- 支持两种模式:重定向前(原始模型,默认)和重定向后(实际模型)
- 使用记录表"模型"列改为"计费模型"列,显示实际计费模型
- 请求详情对话框中添加当前计费模式说明
- 修复 Drizzle 快照链冲突问题(0023/0024 快照父引用错误)
- 完整支持 5 种语言的 i18n 翻译(zh-CN, en, ja, ru, zh-TW)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- 恢复 0022_simple_stardust.sql 迁移文件(枚举类型定义)
- 恢复 0022_snapshot.json 快照文件
- 修复 _journal.json 中的迁移条目顺序(idx 22-26)
- 修复 0023_snapshot.json 的 prevId 指向正确的父快照
- 删除孤儿文件 0023_daily_limit_partial_indexes.sql

修复后的迁移链:
  idx 22: 0022_simple_stardust (枚举类型)
  idx 23: 0023_cheerful_shocker (超时默认值)
  idx 24: 0023_safe_christian_walker (MCP passthrough)
  idx 25: 0024_update_provider_timeout_defaults (超时配置)
  idx 26: 0025_hard_violations (billing_model_source)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
问题:0025_hard_violations 的 when 时间戳 (1764128773876)
小于 0024 的时间戳 (1764206400000),导致 Drizzle 按时间
顺序执行时跳过了这个迁移。

修复:将 0025 的 when 值改为 1764300000000,确保它在
0024 之后执行。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
问题:
- 两个 0023_* 文件导致编号冲突
- journal 时间戳乱序
- 多次手动修改导致迁移链不一致

解决方案:
- 删除冲突迁移文件 (0020-0025)
- 使用 db:generate 重新生成单一迁移 0020_glossy_grandmaster
- 迁移内 SQL 使用幂等语法 (IF NOT EXISTS, DO EXCEPTION)
- 迁移内包含一次性清理旧记录的逻辑

兼容性:
- 新安装:正常执行所有迁移
- 从 0019 升级:执行新迁移
- 从冲突版本升级:清理旧记录 + 幂等迁移

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
问题:0025_hard_violations 的 when 时间戳 (1764128773876)
小于 0024 的时间戳 (1764206400000),导致 Drizzle 按时间
顺序执行时跳过了这个迁移。

修复:将 0025 的 when 值改为 1764300000000,确保它在
0024 之后执行。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- 修复 i18n 参数不匹配: billingDescription 使用 {original}/{current} 替代 {model}
- 修复使用记录表格中重定向标记被 truncate 隐藏的问题
- 简化 error-details-dialog 中的模型重定向 UI: 从三列网格改为紧凑单行设计
- 新增 billingOriginal/billingRedirected i18n 键用于计费标识

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
问题:当请求重试并切换供应商后,统计仍归属到首次选择的失败供应商

修复:
- updateMessageRequestDetails 支持更新 providerId 字段
- 请求成功后更新 provider_id 到最终处理的供应商
- 统计查询使用 providerChain 最后一项(兼容历史数据)

Closes #204

🤖 Generated with [Claude Code](https://claude.ai/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
辅助端点失败不应影响供应商健康状态,避免并发 count_tokens 请求
导致供应商被误熔断的问题。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
问题:翻译键路径不匹配导致返回键本身字符串,Badge 显示超长文本挤压模型名

修复:
- t("modelRedirect.redirected") → t("logs.modelRedirect.redirected")
- t("modelRedirect.actualModelTooltip") → t("logs.details.modelRedirect.actualModelTooltip")
- t("modelRedirect.originalModelTooltip") → t("logs.details.modelRedirect.originalModelTooltip")

🤖 Generated with [Claude Code](https://claude.ai/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- 移除重定向 Badge 中的"已重定向"文字,只保留箭头图标
- 移除有问题的 i18n Tooltip
- 点击图标可打开请求详情弹窗并自动滚动到重定向部分
- ErrorDetailsDialog 支持外部控制打开状态

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- 移除未使用的 isError 变量 (error-details-dialog.tsx)
- 移除未使用的 getActiveSessions 导入 (dashboard-realtime.ts)
- 移除未使用的 Layers 导入 (big-screen/page.tsx)
- 修复 CountUp 组件 useEffect 依赖警告,使用 ref 跟踪前值

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
当供应商 A 配置了模型重定向后失败,切换到供应商 B 时,
系统错误地使用供应商 A 的重定向模型名请求供应商 B。

修复方案:
- ModelRedirector.apply() 使用 getOriginalModel() 获取真正的原始模型
- 当新供应商没有对应重定向规则时,自动重置到原始模型
- 新增 resetToOriginal() 方法处理模型和 Gemini URL 路径重置
- session.ts 新增 originalUrlPathname 字段支持 Gemini 路径重置

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
旧正则 `cache_control.*limit.*blocks` 无法匹配 Anthropic API 返回的错误
"A maximum of 4 blocks with cache_control may be provided",因为消息中
blocks 在 cache_control 之前,且使用 maximum 而非 limit。

新正则支持两种顺序和两个关键词变体。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @ding113, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses several critical bug fixes and introduces new features to enhance the system's stability, accuracy, and configurability. Key improvements include a consolidated and idempotent database migration, the introduction of a configurable billing model source to manage costs more flexibly, and significant refinements to the proxy's model redirection and error handling logic. Additionally, it improves the accuracy of usage statistics and streamlines date filtering in the dashboard.

Highlights

  • Database Schema Updates & Migration Consolidation: A new idempotent Drizzle migration script (0020_glossy_grandmaster.sql) has been added, consolidating previous migrations (0020-0025). This script cleans up old migration records, introduces a daily_reset_mode enum, updates provider default timeouts to 0, and adds new columns like mcp_passthrough_type, mcp_passthrough_url, billing_model_source, and user spending limits (limit_5h_usd, limit_weekly_usd, limit_monthly_usd, limit_concurrent_sessions) to various tables.
  • Configurable Billing Model Source: A new system setting, billingModelSource, has been introduced. This allows administrators to configure whether billing for API requests should be based on the original model requested by the user or the actual model used after any internal redirection. This change is reflected across the UI (dashboard logs, settings) and backend logic.
  • Enhanced Model Redirection Logic: The proxy's model redirection mechanism has been improved. It now correctly resets the model and, for Gemini models, the URL path to the original requested model when switching to a provider that does not have a specific redirection rule for the current model. This ensures consistent behavior during provider retries.
  • Improved Usage Log Date Filtering: The date and time filtering in the usage logs interface has been refactored to handle local time strings directly, eliminating potential timezone conversion issues when querying the database.
  • Refined Error Rules: The regular expressions for 'thinking_error' and 'cache_limit' error categories have been updated to be more robust, catching a wider range of error messages from upstream APIs.
  • Accurate Provider Statistics: The calculation of provider statistics (today's cost and calls) now correctly attributes usage to the final provider in a retry chain, using the provider_chain information to ensure accuracy.
  • Special Handling for count_tokens Requests: count_tokens API requests are now specially handled in the proxy. Errors during these requests will no longer trigger the circuit breaker or cause a provider switch, preventing unintended side effects.
  • Tag Input Blur Commit: The TagInput UI component now automatically commits any pending input value when it loses focus, improving user experience.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@ding113 ding113 added bug Something isn't working size/XL Extra Large PR (> 1000 lines) labels Nov 26, 2025
@ding113
Copy link
Owner Author

ding113 commented Nov 26, 2025

📊 PR Size Analysis

This PR is XL with 5,568 lines changed across 51 files.

⚠️ Large PRs are harder to review and more likely to introduce bugs.

📋 Analysis Breakdown

Migration Files (bulk of deletions):

  • ~4,500 lines are from migration file consolidation
  • 7 old migration files deleted, 1 consolidated migration added
  • This is cleanup work, low review risk

Actual Feature/Bug Fix Changes (~39 files):

  1. Usage logs filtering bug (使用记录的筛选条件那里有错误 #198) - usage-logs-*.tsx, usage-logs.ts
  2. Group not saved bug ([bug] group not saved #201) - tag-input.tsx, transformers, validation
  3. Provider stats mismatch (供应商管理的统计和使用记录有出入 #204) - forwarder.ts, session.ts, message.ts
  4. i18n updates - 10 message JSON files

🔀 Suggested Split

Based on the changes, this PR could be split into:

  1. PR 1: Migration Consolidation

    • drizzle/0020_*.sql, drizzle/meta/*.json
    • Low risk, database maintenance
  2. PR 2: Fix usage logs filtering (使用记录的筛选条件那里有错误 #198)

    • src/repository/usage-logs.ts
    • src/app/[locale]/dashboard/logs/_components/*
    • Related i18n files
  3. PR 3: Fix group not saved ([bug] group not saved #201)

    • src/components/ui/tag-input.tsx
    • src/repository/_shared/transformers.ts
    • src/lib/validation/schemas.ts
  4. PR 4: Fix provider stats mismatch (供应商管理的统计和使用记录有出入 #204)

    • src/app/v1/_lib/proxy/forwarder.ts
    • src/app/v1/_lib/proxy/session.ts
    • src/repository/message.ts

Why Split?

  • Easier to review each bug fix independently
  • Faster CI feedback per change
  • Easier to revert specific fixes if needed
  • Better git history for tracking which fix addressed which issue

🤖 Automated analysis by Claude AI

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a mix of bug fixes, new features, and significant refactoring. The consolidation of database migrations into a single idempotent file is a good step for maintainability, though it appears to have removed the daily limit feature, which needs clarification. The introduction of a configurable billing model source is a valuable new feature. Most importantly, this PR contains critical fixes for the proxy logic, especially regarding model redirection during provider failover and accurate cost attribution in retry scenarios. The UI/UX has also seen several nice improvements. My review focuses on potential regressions from the migration consolidation and suggests minor improvements for maintainability.

Comment on lines 1 to 44
-- 幂等迁移: 合并 0020-0025 的所有变更
-- 此迁移可以在任何状态下安全执行(新安装、从 0019 升级、从冲突版本升级)

-- Step 0: 清理冲突的旧迁移记录(一次性,仅影响从冲突版本升级的用户)
DELETE FROM drizzle.__drizzle_migrations WHERE hash IN (
'0020_next_juggernaut',
'0021_daily_cost_limits',
'0022_simple_stardust',
'0023_cheerful_shocker',
'0023_safe_christian_walker',
'0024_update_provider_timeout_defaults',
'0025_hard_violations'
);
--> statement-breakpoint

-- Step 1: 创建枚举类型(幂等)
DO $$ BEGIN
CREATE TYPE "public"."daily_reset_mode" AS ENUM('fixed', 'rolling');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint

-- Step 2: 更新 providers 表默认值(幂等,无条件安全)
ALTER TABLE "providers" ALTER COLUMN "first_byte_timeout_streaming_ms" SET DEFAULT 0;--> statement-breakpoint
ALTER TABLE "providers" ALTER COLUMN "streaming_idle_timeout_ms" SET DEFAULT 0;--> statement-breakpoint
ALTER TABLE "providers" ALTER COLUMN "request_timeout_non_streaming_ms" SET DEFAULT 0;--> statement-breakpoint

-- Step 3: 添加 keys 表字段(幂等)
ALTER TABLE "keys" ADD COLUMN IF NOT EXISTS "daily_reset_mode" "daily_reset_mode" DEFAULT 'fixed' NOT NULL;--> statement-breakpoint

-- Step 4: 添加 providers 表字段(幂等)
ALTER TABLE "providers" ADD COLUMN IF NOT EXISTS "mcp_passthrough_type" varchar(20) DEFAULT 'none' NOT NULL;--> statement-breakpoint
ALTER TABLE "providers" ADD COLUMN IF NOT EXISTS "mcp_passthrough_url" varchar(512);--> statement-breakpoint
ALTER TABLE "providers" ADD COLUMN IF NOT EXISTS "daily_reset_mode" "daily_reset_mode" DEFAULT 'fixed' NOT NULL;--> statement-breakpoint

-- Step 5: 添加 system_settings 表字段(幂等)
ALTER TABLE "system_settings" ADD COLUMN IF NOT EXISTS "billing_model_source" varchar(20) DEFAULT 'original' NOT NULL;--> statement-breakpoint

-- Step 6: 添加 users 表字段(幂等)
ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "limit_5h_usd" numeric(10, 2);--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "limit_weekly_usd" numeric(10, 2);--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "limit_monthly_usd" numeric(10, 2);--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "limit_concurrent_sessions" integer;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This consolidated migration appears to have removed the limit_daily_usd and daily_reset_time columns from the keys and providers tables. These were part of the now-deleted 0021_daily_cost_limits.sql migration. The associated partial indexes from drizzle/0023_daily_limit_partial_indexes.sql are also missing.

This seems to be an intentional feature removal, but it's a significant change that isn't mentioned in the pull request description. If this was unintentional, it's a critical omission that could lead to loss of functionality. Could you please clarify if this feature is being deprecated? If not, these columns and their indexes should be added back to this migration file.

Comment on lines 1 to 44
-- 幂等迁移: 合并 0020-0025 的所有变更
-- 此迁移可以在任何状态下安全执行(新安装、从 0019 升级、从冲突版本升级)

-- Step 0: 清理冲突的旧迁移记录(一次性,仅影响从冲突版本升级的用户)
DELETE FROM drizzle.__drizzle_migrations WHERE hash IN (
'0020_next_juggernaut',
'0021_daily_cost_limits',
'0022_simple_stardust',
'0023_cheerful_shocker',
'0023_safe_christian_walker',
'0024_update_provider_timeout_defaults',
'0025_hard_violations'
);
--> statement-breakpoint

-- Step 1: 创建枚举类型(幂等)
DO $$ BEGIN
CREATE TYPE "public"."daily_reset_mode" AS ENUM('fixed', 'rolling');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint

-- Step 2: 更新 providers 表默认值(幂等,无条件安全)
ALTER TABLE "providers" ALTER COLUMN "first_byte_timeout_streaming_ms" SET DEFAULT 0;--> statement-breakpoint
ALTER TABLE "providers" ALTER COLUMN "streaming_idle_timeout_ms" SET DEFAULT 0;--> statement-breakpoint
ALTER TABLE "providers" ALTER COLUMN "request_timeout_non_streaming_ms" SET DEFAULT 0;--> statement-breakpoint

-- Step 3: 添加 keys 表字段(幂等)
ALTER TABLE "keys" ADD COLUMN IF NOT EXISTS "daily_reset_mode" "daily_reset_mode" DEFAULT 'fixed' NOT NULL;--> statement-breakpoint

-- Step 4: 添加 providers 表字段(幂等)
ALTER TABLE "providers" ADD COLUMN IF NOT EXISTS "mcp_passthrough_type" varchar(20) DEFAULT 'none' NOT NULL;--> statement-breakpoint
ALTER TABLE "providers" ADD COLUMN IF NOT EXISTS "mcp_passthrough_url" varchar(512);--> statement-breakpoint
ALTER TABLE "providers" ADD COLUMN IF NOT EXISTS "daily_reset_mode" "daily_reset_mode" DEFAULT 'fixed' NOT NULL;--> statement-breakpoint

-- Step 5: 添加 system_settings 表字段(幂等)
ALTER TABLE "system_settings" ADD COLUMN IF NOT EXISTS "billing_model_source" varchar(20) DEFAULT 'original' NOT NULL;--> statement-breakpoint

-- Step 6: 添加 users 表字段(幂等)
ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "limit_5h_usd" numeric(10, 2);--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "limit_weekly_usd" numeric(10, 2);--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "limit_monthly_usd" numeric(10, 2);--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "limit_concurrent_sessions" integer;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The data migration logic from the deleted drizzle/0024_update_provider_timeout_defaults.sql file seems to be missing. The old file contained an UPDATE statement to normalize streaming_idle_timeout_ms for existing providers. While this new migration correctly sets the DEFAULT to 0, it doesn't update existing rows, which could leave inconsistent data in the database for existing installations.

Please consider adding the following UPDATE statement to this migration script to ensure data consistency for users who are upgrading:

-- 批量更新流式静默期超时
-- 规则:
--   - 小于 60000ms (60s) 且大于 0 的 → 改为 60000
--   - 等于 0(不限制)的 → 不操作
--   - 大于等于 60000 的 → 不操作
UPDATE "providers"
SET "streaming_idle_timeout_ms" = 60000
WHERE "streaming_idle_timeout_ms" > 0 
  AND "streaming_idle_timeout_ms" < 60000
  AND "deleted_at" IS NULL;

@ding113
Copy link
Owner Author

ding113 commented Nov 26, 2025

🔒 Security Scan Results

No security vulnerabilities detected

This PR has been scanned against OWASP Top 10, CWE Top 25, and common security anti-patterns. No security issues were identified in the code changes.

📋 Detailed Analysis

Changes Reviewed

  • Database migrations (drizzle/0020_glossy_grandmaster.sql): Idempotent migration consolidating schema changes
  • Repository layer (src/repository/*.ts): Updates to provider statistics, usage logs, system config
  • Proxy layer (src/app/v1/_lib/proxy/*.ts): Model redirector, response handler, session management
  • UI components (src/app/[locale]/**/*.tsx): Error details dialog, model display, tag input, settings form
  • Type definitions (src/types/system-config.ts): New BillingModelSource type
  • Validation schemas (src/lib/validation/schemas.ts): Schema updates for new settings

Scanned Categories

Category Status Notes
A01: Injection (SQL/NoSQL/Command) ✅ Clean All SQL uses parameterized queries via Drizzle ORM sql template literals. The timezone variable comes from validated environment config (TZ with Zod schema). Time strings use normalizeLocalTimeStr() which only performs safe string manipulation.
A02: Broken Authentication ✅ Clean No authentication changes in this PR.
A03: Sensitive Data Exposure ✅ Clean No new logging of sensitive data. No hardcoded secrets.
A04: XML External Entities (XXE) ✅ N/A No XML parsing in changed code.
A05: Broken Access Control ✅ Clean No changes to authorization logic. Provider ID updates properly track the actual handling provider.
A06: Security Misconfiguration ✅ Clean Default values are appropriate. No debug mode enabled.
A07: Cross-Site Scripting (XSS) ✅ Clean React components use standard patterns. No dangerouslySetInnerHTML. User inputs in UI go through proper React escaping.
A08: Insecure Deserialization ✅ Clean JSON parsing only on trusted internal data (provider chain).
A09: Known Vulnerabilities ✅ Clean No new dependencies added.
A10: Logging & Monitoring ✅ Clean Proper logging maintained for debugging.
SSRF ✅ Clean No user-controlled URL fetching in changes.
Path Traversal ✅ Clean No file path operations with user input.

🔍 Specific Findings Analysis

SQL Query Construction (src/repository/usage-logs.ts, src/repository/provider.ts)

  • Pattern: Time filtering uses startDateLocal/endDateLocal strings directly in SQL
  • Assessment: SAFE - The code uses Drizzle ORM's sql template literals which properly parameterize values:
    sql`..AT TIME ZONE ${timezone}..>= ${localTimeStr}::timestamp`
    These are parameterized queries, not string concatenation. Values are escaped by Drizzle.

Timezone Variable (Environment Config)

  • Pattern: const timezone = getEnvConfig().TZ
  • Assessment: SAFE - The TZ variable comes from environment config validated by Zod schema with a safe default (Asia/Shanghai). Not user-controllable.

Time String Normalization (src/repository/usage-logs.ts)

  • Pattern: normalizeLocalTimeStr() function
  • Assessment: SAFE - Only performs string operations (.replace("T", " ")) on datetime-local format strings. No injection risk.

React Components (UI changes)

  • Pattern: New props like onRedirectClick, scrollToRedirect, external dialog controls
  • Assessment: SAFE - Standard React event handling patterns. No direct DOM manipulation with user data.

🛡️ Security Posture

Strong - This PR follows secure coding practices:

  • Uses parameterized queries consistently
  • Validates configuration with Zod schemas
  • Proper React patterns for UI components
  • No introduction of user-controllable data in sensitive operations

🤖 Automated security scan by Claude AI - OWASP Top 10 & CWE coverage

Copy link
Owner Author

@ding113 ding113 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📋 Code Review Summary

This PR consolidates multiple migration files (0020-0025) into a single idempotent migration and includes bug fixes for provider statistics, model redirection, and various UI/UX improvements. The changes are generally well-structured, but there are a few issues that should be addressed.

🔍 Issues Found

  • Critical (🔴): 0 issues
  • High (🟠): 1 issue
  • Medium (🟡): 3 issues
  • Low (🟢): 0 issues

🎯 Priority Actions

  1. 🟠 SQL Query Safety (src/repository/provider.ts): The new SQL query that extracts providerId from provider_chain JSONB using (provider_chain->-1->>'providerId')::int could throw an exception if the JSON structure is malformed or the providerId field contains a non-integer value. Consider adding a fallback:
COALESCE(
  CASE 
    WHEN (provider_chain->-1->>'providerId') ~ '^[0-9]+$' 
    THEN (provider_chain->-1->>'providerId')::int 
    ELSE NULL 
  END,
  provider_id
)
  1. 🟡 Missing Input Validation (src/repository/usage-logs.ts:75-84): The normalizeLocalTimeStr function only validates string length but doesn't validate the actual date format. Malformed date strings could pass through and cause SQL errors.
function normalizeLocalTimeStr(input: string): string {
  // Validate format before processing
  const dateTimeRegex = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}(:\d{2})?$/;
  if (!dateTimeRegex.test(input)) {
    throw new Error(`Invalid date format: ${input}`);
  }
  const normalized = input.replace("T", " ");
  if (normalized.length === 16) {
    return normalized + ":00";
  }
  return normalized;
}
  1. 🟡 Migration Cleanup Safety (drizzle/0020_glossy_grandmaster.sql): The migration deletes records from drizzle.__drizzle_migrations based on hash values. While the comment indicates this is a one-time cleanup for conflicting versions, there's no safeguard if this migration is accidentally re-run or if the hash values match future migrations. Consider adding a version check or timestamp condition.

  2. 🟡 Incomplete ProviderChain Type Handling (src/repository/provider.ts): The SQL uses provider_chain->-1->>'providerId' but the TypeScript ProviderChainItem type in src/types/message.ts defines the field as id, not providerId. This inconsistency could lead to the query always falling back to provider_id:

// ProviderChainItem has:
id: number;  // The field name is 'id', not 'providerId'

The SQL should use provider_chain->-1->>'id' instead.

💡 General Observations

  • The consolidation of migrations 0020-0025 into a single idempotent migration is a good approach for handling version conflicts
  • The model redirector changes for handling provider switches are well-implemented
  • The addition of billingModelSource system setting provides good configurability for cost calculation

🤖 Automated review by Claude AI - focused on identifying issues for improvement

Copy link
Owner Author

@ding113 ding113 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Critical Bug Identified

File: src/repository/provider.ts

The SQL query uses (mr.provider_chain->-1->>'providerId')::int to extract the provider ID from the chain, but the ProviderChainItem interface (in src/types/message.ts) defines the field as id, NOT providerId:

export interface ProviderChainItem {
  id: number;      // <-- The actual field name
  name: string;
  // ...
}

Impact: The SQL condition (mr.provider_chain->-1->>'providerId')::int = p.id will always fail because providerId doesn't exist in the JSON structure. This means:

  • Provider statistics will only count requests where provider_chain is NULL or empty
  • All requests with retry/failover chains will be excluded from statistics
  • The latest_call CTE will produce incorrect results

Suggested fix:
Replace all occurrences of ->>'providerId' with ->>'id':

-- Change from:
(mr.provider_chain->-1->>'providerId')::int = p.id

-- To:
(mr.provider_chain->-1->>'id')::int = p.id

This should be fixed in the following lines of src/repository/provider.ts:

  • Line 380
  • Line 383
  • Line 392
  • Line 395
  • Line 401

合并迁移文件时遗漏了 keys 和 providers 表的每日限额相关字段,导致新安装用户缺少这些列。

添加的字段:
- keys.limit_daily_usd: 每日消费限额
- keys.daily_reset_time: 每日重置时间
- providers.limit_daily_usd: 供应商每日限额
- providers.daily_reset_time: 供应商重置时间

使用 IF NOT EXISTS 确保幂等性,所有升级场景都能正常工作。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@ding113 ding113 merged commit 15bdd25 into main Nov 26, 2025
7 checks passed
@ding113 ding113 added the enhancement New feature or request label Nov 26, 2025
ding113 pushed a commit that referenced this pull request Nov 26, 2025
@ding113
Copy link
Owner Author

ding113 commented Nov 26, 2025

📊 PR Size Analysis

This PR is XL with 5,572 lines changed (574 additions, 4,998 deletions) across 51 files.

Large PRs are harder to review and more likely to introduce bugs.

📝 Analysis Notes

Looking at the changes, I notice that a significant portion of the deletions (~4,500+ lines) come from:

  • Removing 7 conflicting migration files
  • Removing snapshot JSON files

The actual feature/fix changes appear smaller, but the PR still bundles multiple distinct concerns.

🔀 Suggested Split

Based on the changes, this PR could be split into:

  1. PR 1: Migration Consolidation - Consolidate migrations 0020-0025

    • drizzle/0020_glossy_grandmaster.sql (new consolidated migration)
    • drizzle/0020_next_juggernaut.sql (delete)
    • drizzle/0021_daily_cost_limits.sql (delete)
    • drizzle/0023_*.sql files (delete)
    • drizzle/0024_*.sql (delete)
    • drizzle/meta/*.json snapshot updates
    • src/lib/migrate.ts
  2. PR 2: Provider Statistics Fix (供应商管理的统计和使用记录有出入 #204)

    • src/app/v1/_lib/proxy/forwarder.ts
    • src/app/v1/_lib/proxy/session.ts
    • src/repository/message.ts
    • Related test files
  3. PR 3: Group Settings Persistence Fix ([bug] group not saved #201)

    • src/actions/system-config.ts
    • src/repository/system-config.ts
    • src/types/system-config.ts
    • Settings form components
  4. PR 4: Usage Logs Date Filter Fix (使用记录的筛选条件那里有错误 #198)

    • src/app/[locale]/dashboard/logs/_components/ files
    • src/repository/usage-logs.ts
    • Related i18n message updates

Why Split?

  • Easier to review each fix independently
  • Faster CI feedback per change
  • Easier to revert specific fixes if needed
  • Better git history for debugging
  • Migration changes can be merged first to unblock users with upgrade issues

Alternative Approach

If splitting is not practical, at minimum consider:

  • Separating the migration consolidation (PR 1) from the bug fixes
  • This would allow the migration fix to be released quickly to help users with upgrade issues

🤖 Automated analysis by Claude AI

@ding113
Copy link
Owner Author

ding113 commented Nov 26, 2025

🔒 Security Scan Results

No security vulnerabilities detected

This PR has been scanned against OWASP Top 10, CWE Top 25, and common security anti-patterns. No security issues were identified in the code changes.

Changes Analyzed

This PR adds comprehensive usage logs functionality with the following security-relevant components:

  1. Database Queries (src/repository/usage-logs.ts):

    • ✅ Uses Drizzle ORM with parameterized queries - no SQL injection risk
    • ✅ User inputs (filters) are properly bound using eq(), sql template literals with parameter interpolation
    • ✅ Timezone value comes from server-side getEnvConfig().TZ, not user input
  2. Access Control (src/actions/usage-logs.ts):

    • ✅ All endpoints require authentication via getSession()
    • ✅ Non-admin users are restricted to their own data (userId: session.user.id)
    • ✅ Admin role check before allowing broader access
  3. Input Validation (src/lib/validation/schemas.ts):

    • ✅ Zod schemas with appropriate constraints (min/max values, enums)
    • ✅ Integer validation for retention days and batch sizes
    • ✅ Boolean validation for feature flags
  4. Provider Statistics (src/repository/provider.ts):

    • ✅ Uses raw SQL but timezone comes from server config, not user input
    • ✅ No user-controllable data in SQL queries
  5. UI Components (src/components/usage-logs/):

    • ✅ React components with proper data binding (no dangerouslySetInnerHTML)
    • ✅ Data displayed through React's automatic XSS protection

Scanned Categories

  • Injection attacks (SQL, NoSQL, Command, LDAP) - Clean: Uses parameterized queries via Drizzle ORM
  • Authentication and session management - Clean: Session validation on all endpoints
  • Sensitive data exposure - Clean: No credentials or secrets in code
  • Access control and authorization - Clean: Role-based access with user filtering
  • Security misconfiguration - Clean: No debug code or exposed secrets
  • Cross-site scripting (XSS) - Clean: React auto-escaping, no unsafe HTML rendering
  • Insecure deserialization - Clean: Zod schema validation on inputs
  • SSRF and path traversal - N/A: No URL fetching or file operations
  • Cryptographic weaknesses - N/A: No cryptographic operations in changes

OWASP Top 10 Coverage

  • A01: Injection - Clean (parameterized queries)
  • A02: Broken Authentication - Clean (session validation)
  • A03: Sensitive Data Exposure - Clean
  • A04: XML External Entities - N/A (no XML parsing)
  • A05: Broken Access Control - Clean (role-based filtering)
  • A06: Security Misconfiguration - Clean
  • A07: XSS - Clean (React escaping)
  • A08: Insecure Deserialization - N/A (Zod validation)
  • A09: Known Vulnerabilities - Not scoped
  • A10: Logging & Monitoring - Clean (error logging present)

🛡️ Security Posture

Strong - The codebase follows security best practices:

  • Uses ORM with parameterized queries
  • Implements proper authentication and authorization
  • Validates inputs with Zod schemas
  • Server-side configuration for sensitive values

🤖 Automated security scan by Claude AI - OWASP Top 10 & CWE coverage

Copy link
Owner Author

@ding113 ding113 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📋 Code Review Summary

This PR consolidates database migrations (0020-0025) into a single idempotent migration file and adds billing model source configuration feature. The migration consolidation approach is sound.

🔍 Issues Found

  • Critical (🔴): 1 issue
  • High (🟠): 0 issues
  • Medium (🟡): 0 issues
  • Low (🟢): 1 issue

🎯 Priority Actions

  1. Critical: The migration file 0020_glossy_grandmaster.sql is missing the billing_model_source column addition for system_settings table, but the code (schema.ts, repository files, types) expects this column to exist. This will cause runtime errors.

💡 General Observations

The migration consolidation strategy is well-designed with proper idempotent operations using IF NOT EXISTS. However, the new billing_model_source feature added in this PR requires a corresponding migration entry that is currently missing.


🤖 Automated review by Claude AI - focused on identifying issues for improvement


-- Step 5: 添加 system_settings 表字段(幂等)
ALTER TABLE "system_settings" ADD COLUMN IF NOT EXISTS "billing_model_source" varchar(20) DEFAULT 'original' NOT NULL;--> statement-breakpoint

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Critical: Missing billing_model_source column in migration

Why this is a problem: The schema at src/drizzle/schema.ts adds billingModelSource column to system_settings table, and the repository code (src/repository/system-config.ts) queries this field. However, this migration file does not add this column. This will cause database errors for users running this migration:

  • SELECT ... billing_model_source ... will fail with "column does not exist"
  • INSERT statements including this field will fail

Suggested fix: Add this line after Step 5 in the migration:

-- Step 5.1: 添加 system_settings 计费模型来源字段(幂等)
ALTER TABLE "system_settings" ADD COLUMN IF NOT EXISTS "billing_model_source" varchar(20) DEFAULT 'original' NOT NULL;--> statement-breakpoint

* 支持输入格式: "YYYY-MM-DDTHH:mm" 或 "YYYY-MM-DD HH:mm:ss"
*/
function normalizeLocalTimeStr(input: string): string {
// 处理 datetime-local 格式: "2025-11-26T00:00" → "2025-11-26 00:00:00"
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Low: Missing input validation in normalizeLocalTimeStr function

Why this is a problem: The function assumes the input string is always in a valid format (YYYY-MM-DDTHH:mm or YYYY-MM-DD HH:mm:ss). If an unexpected format is passed (e.g., malformed string, empty string, or different format), it will silently produce incorrect results instead of failing early. This could lead to incorrect SQL queries.

Suggested fix:

function normalizeLocalTimeStr(input: string): string {
  // Validate input format
  if (!input || typeof input !== "string") {
    throw new Error("Invalid time string: input is empty or not a string");
  }
  
  // Validate expected formats: "YYYY-MM-DDTHH:mm" (16 chars) or "YYYY-MM-DD HH:mm:ss" (19 chars)
  const trimmed = input.trim();
  if (trimmed.length < 16) {
    throw new Error(`Invalid time string format: ${input}`);
  }
  
  // 处理 datetime-local 格式: "2025-11-26T00:00" → "2025-11-26 00:00:00"
  const normalized = trimmed.replace("T", " ");
  // 如果没有秒数,补充 ":00"
  if (normalized.length === 16) {
    return normalized + ":00";
  }
  return normalized;
}

Copy link
Owner Author

@ding113 ding113 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📋 Code Review Summary (Updated)

This PR adds a new "billing model source" configuration feature that allows administrators to control whether billing uses the original model (before redirection) or the redirected model (after redirection). It also consolidates database migrations, improves time zone handling for log filters, and fixes UI/UX issues.

🔍 Issues Found

  • Critical (🔴): 0 issues
  • High (🟠): 0 issues
  • Medium (🟡): 0 issues
  • Low (🟢): 1 issue

🎯 Priority Actions

  1. Low: Consider adding input validation to normalizeLocalTimeStr function in src/repository/usage-logs.ts to handle edge cases gracefully

💡 General Observations

Well-implemented features:

  • The billing model source feature is properly integrated across the stack (schema, repository, actions, UI)
  • Migration consolidation is well-designed with idempotent operations
  • Time zone handling refactoring from Date objects to local time strings avoids serialization issues
  • The model redirector reset logic handles provider switching correctly
  • CountUp animation fix using useRef prevents stale closure issues
  • TagInput onBlur handler improves UX by committing pending values

Code Quality:

  • Consistent use of TypeScript types (BillingModelSource type)
  • Proper i18n support across all 5 languages
  • UI accessibility improvements with scroll-to-section functionality

🤖 Automated review by Claude AI - focused on identifying issues for improvement

Copy link
Owner Author

@ding113 ding113 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📋 Code Review Summary (Updated)

This PR adds a new "billing model source" configuration feature that allows administrators to control whether billing uses the original model (before redirection) or the redirected model (after redirection). It also consolidates database migrations, improves time zone handling for log filters, and fixes UI/UX issues.

🔍 Issues Found

  • Critical (🔴): 0 issues
  • High (🟠): 0 issues
  • Medium (🟡): 0 issues
  • Low (🟢): 1 issue

🎯 Priority Actions

  1. Low: Consider adding input validation to normalizeLocalTimeStr function in src/repository/usage-logs.ts to handle edge cases gracefully

💡 General Observations

Well-implemented features:

  • The billing model source feature is properly integrated across the stack (schema, repository, actions, UI)
  • Migration consolidation is well-designed with idempotent operations
  • Time zone handling refactoring from Date objects to local time strings avoids serialization issues
  • The model redirector reset logic handles provider switching correctly
  • CountUp animation fix using useRef prevents stale closure issues
  • TagInput onBlur handler improves UX by committing pending values

Code Quality:

  • Consistent use of TypeScript types (BillingModelSource type)
  • Proper i18n support across all 5 languages
  • UI accessibility improvements with scroll-to-section functionality

🤖 Automated review by Claude AI - focused on identifying issues for improvement

sususu98 pushed a commit to sususu98/claude-code-hub that referenced this pull request Nov 28, 2025
ding113 pushed a commit that referenced this pull request Nov 28, 2025
ding113 pushed a commit that referenced this pull request Nov 28, 2025
@ding113 ding113 mentioned this pull request Nov 28, 2025
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working enhancement New feature or request size/XL Extra Large PR (> 1000 lines)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

供应商管理的统计和使用记录有出入 [bug] group not saved 使用记录的筛选条件那里有错误

1 participant

Comments